package com.bd.demo.model

import android.Manifest
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.bd.demo.app.MyApp
import com.bd.demo.bean.MessageInfo
import com.bd.demo.ui.hasPermissions
import com.clj.fastble.data.BleDevice
import com.d10ng.coroutines.launchMain
import com.d10ng.datelib.curTime
import com.hailiao.bdproto2p1.BDProto2p1
import com.hailiao.hlbdcore.HLBDCore
import com.hailiao.hlbdcore.annotation.HLBDReceive
import com.hailiao.hlbdcore.annotation.HLBDSend
import com.hailiao.hlbdcore.base.BaseBDProto
import com.hailiao.hlbdcore.base.BaseInBean
import com.hailiao.hlbdcore.base.BaseOutBean
import com.hailiao.hlbdcore.base.HLBDClient
import com.hailiao.hlbdcore.constant.Core
import com.hailiao.hlbluetoothlib.HLBleManager

class BleViewModel: ViewModel() {

    init {
        // 初始化协议
        BDProto2p1.init()
        // 注册协议监听器
        HLBDCore.register(this)
    }

    override fun onCleared() {
        // 反注册
        HLBDCore.unRegister(this)
        super.onCleared()
    }

    /** 定位权限标记 */
    private val locationPermission = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)

    /** 检查定位权限 */
    @Synchronized
    fun checkLocationPermission() {
        launchMain {
            val act = MyApp.instance().topActivityLiveData.value
            if (act?.hasPermissions(locationPermission) == true) return@launchMain
            act?.reqPermissions(locationPermission)
        }
    }

    /** 蓝牙模块 */
    val ble: HLBleManager = HLBleManager.instance(MyApp.instance()).apply {
        setLogEnable(true)
    }

    /** 连接蓝牙设备信息 */
    var bleDeviceInfo: BleDevice? = null

    fun getMac(): String =
        bleDeviceInfo?.mac?: ""

    /** 消息列表 */
    val msgList: MutableLiveData<List<MessageInfo>> = MutableLiveData(listOf())

    /** 是否正在刷新 */
    val isRefreshing: MutableLiveData<Boolean> = MutableLiveData(false)

    /**
     * 刷新设备
     */
    suspend fun refreshBleDevices() {
        isRefreshing.postValue(true)
        checkLocationPermission()
        ble.scanDevice()
        isRefreshing.postValue(false)
    }

    /**
     * 连接设备
     * @param device BleDevice
     * @param fail Function1<String, Unit>
     * @param success Function0<Unit>
     */
    suspend fun connectBleDevice(device: BleDevice, fail:(String) -> Unit, success:() -> Unit) {
        val isConnected = ble.connectDevice(device)
        if (!isConnected) {
            fail.invoke("连接失败！")
            return
        }
        val isNotify = ble.startNotify(device)
        if (!isNotify) {
            fail.invoke("打开通知失败！")
            return
        }
        bleDeviceInfo = device
        success.invoke()
    }

    /**
     * 添加
     * @param msg MessageInfo
     */
    @Synchronized
    fun addMsg(msg: MessageInfo) {
        val value = msgList.value?.toMutableList()?: mutableListOf()
        value.add(0, msg)
        msgList.postValue(value.subList(0, value.size.coerceAtMost(100)))
    }

    /**
     * 监听发出的指令
     * @param client HLBDClient
     * @param device String
     * @param raw ByteArray
     * @param isCanAnalysis Boolean
     * @param proto BaseBDProto
     * @param bean BaseInBean
     */
    @HLBDSend
    fun onHLSend(
        client: HLBDClient,
        device: String,
        raw: ByteArray,
        isCanAnalysis: Boolean,
        proto: BaseBDProto,
        bean: BaseInBean
    ) {
        val msg = MessageInfo(
            curTime, false, raw.toString(Core.GBK_CHARSET), bean.toReadString()
        )
        addMsg(msg)
    }

    /**
     * 监听接收的指令
     * @param client HLBDClient
     * @param device String
     * @param raw ByteArray
     * @param isCanAnalysis Boolean
     * @param proto BaseBDProto
     * @param bean BaseOutBean
     */
    @HLBDReceive
    fun onHLReceive(
        client: HLBDClient,
        device: String,
        raw: ByteArray,
        isCanAnalysis: Boolean,
        proto: BaseBDProto,
        bean: BaseOutBean
    ) {
        val msg = MessageInfo(
            curTime, true, raw.toString(Core.GBK_CHARSET), bean.toReadString()
        )
        addMsg(msg)
    }
}